use bitops::{extract32, make_64bit_mask};

use super::{
    common::{GIC_INTERNAL, GIC_MAXIRQ, GIC_NCPU, GIC_NR_SGIS},
    GICv2StateInner,
};

pub(crate) const ALL_CPU_MASK: usize = (1 << GIC_NCPU) - 1;

impl GICv2StateInner {
    pub(crate) fn gic_dist_set_enabled(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].enabled |= cm;
    }

    #[inline]
    pub(crate) fn gic_dist_clear_enabled(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].enabled &= !cm;
    }

    #[inline]
    pub(crate) fn gic_dist_test_enabled(&self, irq: usize, cm: u8) -> bool {
        self.irq_state[irq].enabled & cm != 0
    }

    #[inline]
    pub(crate) fn gic_dist_set_pending(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].pending |= cm;
    }

    #[inline]
    pub(crate) fn gic_dist_clear_pending(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].pending &= !cm;
    }

    #[inline]
    pub(crate) fn gic_dist_test_pending(&self, irq: usize, cm: u8) -> bool {
        self.irq_state[irq].pending & cm != 0
    }

    #[inline]
    pub(crate) fn gic_dist_set_active(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].active |= cm;
    }

    #[inline]
    pub(crate) fn gic_dist_clear_active(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].active &= !cm;
    }

    #[inline]
    pub(crate) fn gic_dist_test_active(&self, irq: usize, cm: u8) -> bool {
        self.irq_state[irq].active & cm != 0
    }

    #[inline]
    pub(crate) fn gic_dist_set_model(&mut self, irq: usize) {
        self.irq_state[irq].model = true;
    }

    #[inline]
    pub(crate) fn gic_dist_clear_model(&mut self, irq: usize) {
        self.irq_state[irq].model = false;
    }

    #[inline]
    pub(crate) fn gic_dist_test_model(&self, irq: usize) -> bool {
        self.irq_state[irq].model
    }

    #[inline]
    pub(crate) fn gic_dist_set_level(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].level |= cm;
    }

    #[inline]
    pub(crate) fn gic_dist_clear_level(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].level &= !cm;
    }

    #[inline]
    pub(crate) fn gic_dist_test_level(&self, irq: usize, cm: u8) -> bool {
        self.irq_state[irq].level & cm != 0
    }

    #[inline]
    pub(crate) fn gic_dist_set_edge_trigger(&mut self, irq: usize) {
        self.irq_state[irq].edge_trigger = true;
    }

    #[inline]
    pub(crate) fn gic_dist_clear_edge_trigger(&mut self, irq: usize) {
        self.irq_state[irq].edge_trigger = false;
    }

    #[inline]
    pub(crate) fn gic_dist_test_edge_trigger(&self, irq: usize) -> bool {
        self.irq_state[irq].edge_trigger
    }

    #[inline]
    pub(crate) fn gic_dist_get_priority_i(&self, irq: usize, cpu: usize) -> u8 {
        if irq < GIC_INTERNAL {
            self.priority1[irq][cpu]
        } else {
            self.priority2[irq - GIC_INTERNAL]
        }
    }

    #[inline]
    pub(crate) fn gic_dist_target(&self, irq: usize) -> u8 {
        self.irq_target[irq]
    }

    #[inline]
    pub(crate) fn gic_dist_clear_group(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].group &= !cm;
    }

    #[inline]
    pub(crate) fn gic_dist_set_group(&mut self, irq: usize, cm: u8) {
        self.irq_state[irq].group |= cm;
    }

    #[inline]
    pub(crate) fn gic_dist_test_group(&self, irq: usize, cm: u8) -> bool {
        self.irq_state[irq].group & cm != 0
    }
}

pub(crate) const GICD_CTLR_EN_GRP0: usize = 1 << 0;
pub(crate) const GICD_CTLR_EN_GRP1: usize = 1 << 1;

pub(crate) const GICC_CTLR_EN_GRP0: usize = 1 << 0;
pub(crate) const GICC_CTLR_EN_GRP1: usize = 1 << 1;
pub(crate) const GICC_CTLR_ACK_CTL: usize = 1 << 2;
pub(crate) const GICC_CTLR_FIQ_EN: usize = 1 << 3;
// GICv1: SBPR
pub(crate) const GICC_CTLR_CBPR: usize = 1 << 4;
pub(crate) const GICC_CTLR_EOIMODE: usize = 1 << 9;
pub(crate) const GICC_CTLR_EOIMODE_NS: usize = 1 << 10;

// 寄存器字段定义
pub(crate) const R_GICH_HCR_EN_MASK: u32 = make_64bit_mask(0, 1) as u32;
pub(crate) const R_GICH_HCR_UIE_MASK: u32 = make_64bit_mask(1, 1) as u32;
pub(crate) const R_GICH_HCR_LRENPIE_MASK: u32 = make_64bit_mask(2, 1) as u32;
pub(crate) const R_GICH_HCR_NPIE_MASK: u32 = make_64bit_mask(3, 1) as u32;
pub(crate) const R_GICH_HCR_VGRP0EIE_MASK: u32 = make_64bit_mask(4, 1) as u32;
pub(crate) const R_GICH_HCR_VGRP0DIE_MASK: u32 = make_64bit_mask(5, 1) as u32;
pub(crate) const R_GICH_HCR_VGRP1EIE_MASK: u32 = make_64bit_mask(6, 1) as u32;
pub(crate) const R_GICH_HCR_VGRP1DIE_MASK: u32 = make_64bit_mask(7, 1) as u32;
pub(crate) const R_GICH_HCR_EOICount_SHIFT: u32 = 27;
pub(crate) const R_GICH_HCR_EOICount_MASK: u32 = make_64bit_mask(27, 5) as u32;

pub(crate) const GICH_HCR_MASK: u32 = R_GICH_HCR_EN_MASK
    | R_GICH_HCR_UIE_MASK
    | R_GICH_HCR_LRENPIE_MASK
    | R_GICH_HCR_NPIE_MASK
    | R_GICH_HCR_VGRP0EIE_MASK
    | R_GICH_HCR_VGRP0DIE_MASK
    | R_GICH_HCR_VGRP1EIE_MASK
    | R_GICH_HCR_VGRP1DIE_MASK
    | R_GICH_HCR_EOICount_MASK;

pub(crate) const R_GICH_LR0_VirtualID_MASK: u32 = make_64bit_mask(0, 10) as u32;
pub(crate) const R_GICH_LR0_PhysicalID_MASK: u32 = make_64bit_mask(10, 10) as u32;
pub(crate) const R_GICH_LR0_CPUID_MASK: u32 = make_64bit_mask(10, 3) as u32;
pub(crate) const R_GICH_LR0_EOI_MASK: u32 = make_64bit_mask(19, 1) as u32;
pub(crate) const R_GICH_LR0_Priority_MASK: u32 = make_64bit_mask(23, 5) as u32;
pub(crate) const R_GICH_LR0_State_MASK: u32 = make_64bit_mask(28, 2) as u32;
pub(crate) const R_GICH_LR0_Grp1_MASK: u32 = make_64bit_mask(30, 1) as u32;
pub(crate) const R_GICH_LR0_HW_MASK: u32 = make_64bit_mask(31, 1) as u32;

pub(crate) const R_GICH_MISR_EOI_MASK: u32 = make_64bit_mask(0, 1) as u32;
pub(crate) const R_GICH_MISR_U_MASK: u32 = make_64bit_mask(1, 1) as u32;
pub(crate) const R_GICH_MISR_LRENP_MASK: u32 = make_64bit_mask(2, 1) as u32;
pub(crate) const R_GICH_MISR_NP_MASK: u32 = make_64bit_mask(3, 1) as u32;
pub(crate) const R_GICH_MISR_VGrp0E_MASK: u32 = make_64bit_mask(4, 1) as u32;
pub(crate) const R_GICH_MISR_VGrp0D_MASK: u32 = make_64bit_mask(5, 1) as u32;
pub(crate) const R_GICH_MISR_VGrp1E_MASK: u32 = make_64bit_mask(6, 1) as u32;
pub(crate) const R_GICH_MISR_VGrp1D_MASK: u32 = make_64bit_mask(7, 1) as u32;

pub(crate) const GICH_LR_MASK: u32 = R_GICH_LR0_VirtualID_MASK
    | R_GICH_LR0_PhysicalID_MASK
    | R_GICH_LR0_CPUID_MASK
    | R_GICH_LR0_EOI_MASK
    | R_GICH_LR0_Priority_MASK
    | R_GICH_LR0_State_MASK
    | R_GICH_LR0_Grp1_MASK
    | R_GICH_LR0_HW_MASK;

pub(crate) const R_GICH_LR0_State_SHIFT: u32 = 28;

pub(crate) const GICH_LR_STATE_INVALID: u32 = 0;
pub(crate) const GICH_LR_STATE_PENDING: u32 = 1;
pub(crate) const GICH_LR_STATE_ACTIVE: u32 = 2;
pub(crate) const GICH_LR_STATE_ACTIVE_PENDING: u32 = 3;

#[inline]
pub(crate) fn gich_lr_virt_id(entry: u32) -> u32 {
    extract32(entry, 0, 10)
}

#[inline]
pub(crate) fn gich_lr_phys_id(entry: u32) -> u32 {
    extract32(entry, 10, 10)
}

#[inline]
pub(crate) fn gich_lr_cpuid(entry: u32) -> u32 {
    extract32(entry, 10, 3)
}

#[inline]
pub(crate) fn gich_lr_eoi(entry: u32) -> u32 {
    extract32(entry, 19, 1)
}

#[inline]
pub(crate) fn gich_lr_priority(entry: u32) -> u32 {
    extract32(entry, 23, 5) << 3
}

#[inline]
pub(crate) fn gich_lr_state(entry: u32) -> u32 {
    extract32(entry, 28, 2)
}

#[inline]
pub(crate) fn gich_lr_group(entry: u32) -> u32 {
    extract32(entry, 30, 1)
}

#[inline]
pub(crate) fn gich_lr_hw(entry: u32) -> u32 {
    extract32(entry, 31, 1)
}

#[inline]
pub(crate) fn gich_lr_clear_pending(entry: &mut u32) {
    *entry = !(GICH_LR_STATE_PENDING << R_GICH_LR0_State_SHIFT);
}

#[inline]
pub(crate) fn gich_lr_set_active(entry: &mut u32) {
    *entry |= GICH_LR_STATE_ACTIVE << R_GICH_LR0_State_SHIFT;
}

#[inline]
pub(crate) fn gich_lr_clear_active(entry: &mut u32) {
    *entry &= !(GICH_LR_STATE_ACTIVE << R_GICH_LR0_State_SHIFT);
}

pub(crate) const GICC_CTLR_V1_MASK: u32 = 0x1;
pub(crate) const GICC_CTLR_V1_S_MASK: u32 = 0x1f;
pub(crate) const GICC_CTLR_V2_MASK: u32 = 0x21f;
pub(crate) const GICC_CTLR_V2_S_MASK: u32 = 0x61f;

// 这个特殊情况用于 revision 属性
pub(crate) const REV_11MPCORE: usize = 0;

impl GICv2StateInner {
    #[inline]
    pub(crate) fn gic_test_pending(&self, irq: usize, cm: u8) -> bool {
        if self.revision == REV_11MPCORE as u32 {
            (self.irq_state[irq].pending & cm) != 0
        } else {
            (self.irq_state[irq].pending & cm) != 0
                || (!self.gic_dist_test_edge_trigger(irq) && self.gic_dist_test_level(irq, cm))
        }
    }

    #[inline]
    pub(crate) fn gic_is_vcpu(&self, cpu: usize) -> bool {
        cpu >= GIC_NCPU
    }

    #[inline]
    pub(crate) fn gic_get_vcpu_real_id(&self, cpu: usize) -> usize {
        if cpu >= GIC_NCPU { cpu - GIC_NCPU } else { cpu }
    }

    // 如果给定的 vIRQ 状态存在于 LR 中, 并且处于活动状态或挂起并处于活动状态, 则返回 true
    // 此函数用于检查来宾的"中断结束"或"中断停用"请求是否有效, 并与已确认的 vIRQ 的 LR 匹配
    // (即在其状态中设置了活动位)
    #[inline]
    pub(crate) fn gic_virq_is_valid(&self, irq: usize, vcpu: usize) -> bool {
        let cpu = self.gic_get_vcpu_real_id(vcpu);

        for lr_idx in 0..self.num_lrs as usize {
            let entry = &self.h_lr[lr_idx][cpu];
            if gich_lr_virt_id(*entry) == irq as u32
                && gich_lr_state(*entry) & GICH_LR_STATE_ACTIVE != 0
            {
                return true;
            }
        }
        false
    }

    // 返回与给定 vIRQ 匹配的 LR 条目的引用
    //
    // 此函数用于检索我们确定对应的 vIRQ 存在于当前上下文中的 LR(即其当前状态不是`invalid`):
    // - 相应的 vIRQ 已经通过 `gic_virq_is_valid()` 进行了验证, 因此它是"活动"或"活动和待定";
    // - 或者它正在挂起并且已经被 `gic_get_best_virq()` 选中. 它现在是 `挂起`,`活动`或者`活动并挂起`,
    // 这取决于来宾已经对这个 vIRQ 做了什么.
    //
    // 具有相同 VirtualID 的多个 LRs 会导致 GIC 中的不可预测行为. 我们选择返回第一个匹配的.
    #[inline]
    pub(crate) fn gic_get_lr_entry(&mut self, irq: usize, vcpu: usize) -> &mut u32 {
        let cpu = self.gic_get_vcpu_real_id(vcpu);

        for lr_idx in 0..self.num_lrs as usize {
            let entry = &self.h_lr[lr_idx][cpu];
            if gich_lr_virt_id(*entry) == irq as u32
                && gich_lr_state(*entry) != GICH_LR_STATE_INVALID
            {
                return &mut self.h_lr[lr_idx][cpu];
            }
        }

        unreachable!()
    }

    #[inline]
    pub(crate) fn gic_test_group(&mut self, irq: usize, cpu: usize) -> bool {
        if self.gic_is_vcpu(cpu) {
            let entry = self.gic_get_lr_entry(irq, cpu);
            gich_lr_group(*entry) != 0
        } else {
            self.gic_dist_test_group(irq, 1 << cpu)
        }
    }

    #[inline]
    pub(crate) fn gic_clear_pending(&mut self, irq: usize, cpu: usize) {
        if self.gic_is_vcpu(cpu) {
            let entry = self.gic_get_lr_entry(irq, cpu);
            gich_lr_clear_pending(entry);
        }
    }

    #[inline]
    pub(crate) fn gic_set_active(&mut self, irq: usize, cpu: usize) {
        if self.gic_is_vcpu(cpu) {
            let entry = self.gic_get_lr_entry(irq, cpu);
            gich_lr_set_active(entry);
        } else {
            self.gic_dist_set_active(irq, 1 << cpu);
        }
    }

    #[inline]
    pub(crate) fn gic_clear_active(&mut self, irq: usize, cpu: usize) {
        if self.gic_is_vcpu(cpu) {
            let entry = self.gic_get_lr_entry(irq, cpu);
            gich_lr_clear_active(entry);

            if gich_lr_hw(*entry) != 0 {
                // 硬件中断. 我们必须将停用请求转发给分发器
                let phys_irq = gich_lr_phys_id(*entry);
                let rcpu = self.gic_get_vcpu_real_id(cpu);

                if phys_irq < GIC_NR_SGIS as u32 || phys_irq >= GIC_MAXIRQ as u32 {
                    // 不可预测的行为, 我们选择忽略请求
                    return;
                }

                // 这相当于在物理 cpu 接口上向 DIR 写入一个 NS write. 因此, 如果 GIC 是安全的,
                // 则忽略 group0 中断去激活.
                if !self.security_extn || self.gic_dist_test_group(phys_irq as usize, 1 << rcpu) {
                    self.gic_dist_clear_active(phys_irq as usize, 1 << rcpu);
                }
            }
        } else {
            self.gic_dist_clear_active(irq, 1 << cpu);
        }
    }

    #[inline]
    pub(crate) fn gic_get_priority(&mut self, irq: usize, cpu: usize) -> u8 {
        if self.gic_is_vcpu(cpu) {
            let entry = self.gic_get_lr_entry(irq, cpu);
            gich_lr_priority(*entry) as u8
        } else {
            self.gic_dist_get_priority_i(irq, cpu)
        }
    }
}
